Object.extend(Event, {
	wheel:function (event){
		var delta = 0;
		if (!event) event = window.event;
		if (event.wheelDelta) {
			delta = event.wheelDelta/120; 
			if (window.opera) delta = -delta;
		} else if (event.detail) { delta = -event.detail/3;	}
		return Math.round(delta); //Safari Round
	}
});

Slot = Class.create({

	SLOT_LEFT_WIDTH		: 7,
	SLOT_RIGHT_WIDTH	: 7,
	ROW_HEIGHT			: 44,

	initialize: function(container, values) {
		this.changeListeners = new Array();
		this.initializeContainer(container);
		this.createUI();
		this.populateValues(values);
		this.start();
	},
	
	initializeContainer: function(container) {
		this.container = $(container);
		this.width		= this.container.getWidth();
		this.netWidth	= this.width - this.SLOT_LEFT_WIDTH - this.SLOT_RIGHT_WIDTH;
	},
	
	addChangeListener: function(listener) {
		if (listener.slotChanged)
		{
			this.changeListeners.push(listener);
		}
	},
	
	removeChangeListener: function(listener) {
		this.changeListeners = this.changeListeners.without(listener);
	},
	
	notifyChange: function(index) {
		var item = this.items[index];
		this.changeListeners.each(function(listener) {
			listener.slotChanged(this, item);
		}.bind(this));
	},
	
	createUI: function() {
		this.createSlotInnerContainer();
		this.createSlotBackgroundContainer();
		this.createSlotDataContainer();
	},
	
	createSlotInnerContainer: function() {
		this.slotContainer = new Element("DIV", {className: "slotInnerContainer"});
		this.container.insert(this.slotContainer);
	},
	
	createSlotBackgroundContainer: function() {
		this.slotBackgroundContainer = new Element("DIV", {className: "slotBackgroundContainer"});
		this.slotContainer.insert(this.slotBackgroundContainer);
		this.slotLeft 	= this.createSlotBackground("slotLeft");
		this.slotMid 	= this.createSlotBackground("slotMid", this.netWidth);
		this.slotRight 	= this.createSlotBackground("slotRight");
	},
	
	createSlotBackground: function(c, w) {
		var slotElement = new Element("DIV", {className : c});
		if (w)
		{
			slotElement.setStyle({ width : w + "px"});
		}
		this.slotBackgroundContainer.insert(slotElement);
		return slotElement;
	},
	
	createSlotDataContainer: function() {
		this.slotDataContainer = new Element("DIV", {className: "slotDataContainer"});
		this.slotContainer.insert(this.slotDataContainer);
	},
	
	populateValues: function(values) {
		this.count = 0;
		if (values)
		{
			
			this.items			= new Array();
			this.count 			= values.length;
			var selectedValue 	= 0;
			this.data 			= new Element("UL");
			for (var index = 0; index < values.length; index++)
			{
				this.createValueDisplayer(values[index], index);
				if (values[index].selected)
				{
					selectedValue = index;
				}
			}
			this.slotDataContainer.insert(this.data);
			this.setSelectedIndex(selectedValue);
		}
	},
	
	createValueDisplayer: function(value, index) {
		var li 		= new Element("LI");
		li.index	= index;
		li.value 	= value.value;
		li.update(value.text);
		this.items.push(li);
		this.data.insert(li);
	},
	
	hookEvents: function() {
		if (!this.mouseDownHandler)
		{
			this.mouseDownHandler 	= this.mouseDown.bindAsEventListener(this);
			this.mouseMoveHandler 	= this.mouseMove.bindAsEventListener(this);
			this.mouseUpHandler 	= this.mouseUp.bindAsEventListener(this);
			this.mouseWheelHandler	= this.mouseWheel.bindAsEventListener(this);
		}
	},
	
	start: function() {
		if (this.count > 0)
		{
			this.hookEvents();
			this.container.observe("mousedown", 		this.mouseDownHandler);
			this.container.observe("mousemove", 		this.mouseMoveHandler);
			this.container.observe("mouseup", 			this.mouseUpHandler);
			this.container.observe("mousewheel",		this.mouseWheelHandler);
			this.container.observe("DOMMouseScroll",	this.mouseWheelHandler);
		}
	},
	
	stop: function() {
		if (this.count > 0)
		{
			this.container.stopObserving("mousedown", 		this.mouseDownHandler);
			this.container.stopObserving("mousemove", 		this.mouseMoveHandler);
			this.container.stopObserving("mouseup", 		this.mouseUpHandler);
			this.container.stopObserving("mousewheel",		this.mouseWheelHandler);
			this.container.stopObserving("DOMMouseScroll",	this.mouseWheelHandler);
		}
	},
	
	listenDocumentEvents: function() {
		Event.observe(document, "mousemove", 	this.mouseMoveHandler);
		Event.observe(document, "mouseup", 		this.mouseUpHandler);
	},
	
	stopListeningDocumentEvents: function() {
		Event.stopObserving(document, "mousemove", 	this.mouseMoveHandler);
		Event.stopObserving(document, "mouseup", 	this.mouseUpHandler);
	},
	
	minMaxCheck: function(index) {
		var result = index >= 0 && index < this.count
		if (result && this.minIndex)
		{
			result = index >= this.minIndex;
		}
		if (result && this.maxIndex)
		{
			result = index <= this.maxIndex;
		}
		return result;
	},
	
	mouseWheel: function(e) {
		var delta 		= Event.wheel(e);
		var	newIndex	= this.selectedIndex - delta;
		if (this.minMaxCheck(newIndex))
		{
			delta *= this.ROW_HEIGHT;
			var y = parseInt(this.data.getStyle("top"));
			this.data.setStyle({top : (y + delta) + "px"});
			this.selectedIndex 	= newIndex;
			this.notifyChange(this.selectedIndex);
			Event.stop(e);
		}
	},
	
	mouseDown: function(e) {
		this.dragging	= true;
		this.startPos	= Event.pointer(e);
		this.stopPos	= this.startPos;
		this.listenDocumentEvents();
		Event.stop(e);
	},
	
	mouseMove: function(e) {
		if (this.dragging)
		{
			this.stopPos = Event.pointer(e);
			var delta 		= this.stopPos.y - this.startPos.y;
			var absDelta 	= Math.abs(delta);
			var rowCount	= 0;
			var newIndex	= 0;
			if (absDelta > this.ROW_HEIGHT)
			{
				rowCount	= parseInt(delta / this.ROW_HEIGHT);
				newIndex	= this.selectedIndex - rowCount;
				if (this.minMaxCheck(newIndex))
				{
					this.selectedIndex 	= newIndex;
					delta 				= (rowCount * this.ROW_HEIGHT);
					var y 				= parseInt(this.data.getStyle("top"));
					this.data.setStyle({top : (y + delta) + "px"});
					this.startPos = this.stopPos;
					this.notifyChange(this.selectedIndex);
				}
			}
			Event.stop(e);
		}
	},
	
	mouseUp: function(e) {
		if (this.dragging)
		{
			this.stopListeningDocumentEvents();
			this.dragging = false;
			Event.stop(e);
		}
	},
	
	setSelectedIndex: function(index) {
		if (index >= 0 && index < this.count)
		{
			var y = -((index-2) * this.ROW_HEIGHT);
			this.data.setStyle({top : y + "px"});
			this.selectedIndex = index;
			this.notifyChange(index);
		}
	},
	
	select: function(value) {
		if (this.count > 0)
		{
			for (var index = 0; index < this.items.length; index++)
			{
				if (this.items[index].value == value)
				{
					this.setSelectedIndex(index);
					return;
				}
			}
		}
	},
	
	getValue: function() {
		return this.count > 0 && this.selectedIndex && this.selectedIndex >= 0 && this.selectedIndex < this.count ? this.items[this.selectedIndex].value : null;
	},
	
	setValue: function(value) {
		this.select(value);
	},
	
	setMinIndex: function(index) {
		if (this.count > 0)
		{
			this.minIndex = index;
			if (this.selectedIndex < this.minIndex) 
			{
				this.setSelectedIndex(this.minIndex);
			}
		}
	},
	
	setMaxIndex: function(index) {
		if (this.count > 0)
		{
			this.maxIndex = index;
			if (this.selectedIndex > this.maxIndex) 
			{
				this.setSelectedIndex(this.maxIndex);
			}
		}
	}

});

NumericSlot = Class.create(Slot, {

	initialize: function($super, container, minValue, maxValue, padTo) {
		$super(container);
		this.initializeNumericValues(minValue, maxValue, padTo);
	},
	
	initializeNumericValues: function(minValue, maxValue, padTo) {
		var values = new Array();
		for (var index = minValue; index <= maxValue; index++)
		{
			values.push({
				value 		: index,
				text		: padTo ? index.toPaddedString(padTo) : index,
				selected	: index == 1
			});
		}
		this.populateValues(values);
		this.start();
	}
	
});

DaySlot = Class.create(NumericSlot, {

	initialize: function($super, container) {
		$super(container, 1, 31, 2);
	}
	
});

MonthSlot = Class.create(Slot, {

	MONTHS	: ["January","February","March","April","May","June","July","August","September","October","November","December"],
	
	initialize: function($super, container) {
		$super(container);
		this.initializeMonthValues();
	},
	
	initializeMonthValues: function() {
		var values = new Array();
		for (var index = 0; index < this.MONTHS.length; index++)
		{
			values.push({
				value 	: index,
				text	: this.MONTHS[index]
			});
		}
		this.populateValues(values);
		this.start();
	}
	
});

YearSlot = Class.create(Slot, {

	initialize: function($super, container, minYear, maxYear) {
		$super(container);
		this.initializeYearValues(minYear, maxYear);
	},
	
	initializeYearValues: function(minYear, maxYear) {
		var values = new Array();
		for (var index = minYear; index <= maxYear; index++)
		{
			values.push({
				value 		: index,
				text		: index,
				selected	: index == minYear
			});
		}
		this.populateValues(values);
		this.start();
	}
	
});

DateSlots = Class.create({

	SLOT_BORDER_WIDTH	: 14,
	DAY_SLOT_WIDTH		: 50,
	MONTH_SLOT_WIDTH	: 130,
	YEAR_SLOT_WIDTH		: 70,
	TOTAL_WIDTH			: 0,
	TOTAL_GAP_WIDTH		: 30,
	MIN_HEIGHT			: 215,

	initialize: function(container) {
		this.initializeWidths();
		this.initializeContainer(container);
		this.createSlotGap(10);
		this.createDaySlot();
		this.createSlotGap(5);
		this.createMonthSlot();
		this.createSlotGap(5);
		this.createYearSlot();
		this.createSlotGap(10);
		this.setValue(new Date());
	},
	
	initializeContainer: function(container) {
		this.container 	= $(container);
		var dimensions	= this.container.getDimensions();
		if (dimensions.width < this.TOTAL_WIDTH)
		{
			this.container.setStyle({width: this.TOTAL_WIDTH + "px"});
		}
		if (dimensions.height < this.MIN_HEIGHT)
		{
			this.container.setStyle({height: this.MIN_HEIGHT + "px"});
		}
	},
	
	initializeWidths: function() {
		this.DAY_SLOT_WIDTH		+= this.SLOT_BORDER_WIDTH;
		this.MONTH_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.YEAR_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.TOTAL_WIDTH		= this.DAY_SLOT_WIDTH + this.MONTH_SLOT_WIDTH + this.YEAR_SLOT_WIDTH + this.TOTAL_GAP_WIDTH;
	},
	
	createDaySlot: function() {
		this.daySlotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.DAY_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(this.daySlotContainer);
		this.daySlot = new DaySlot(this.daySlotContainer);
		this.daySlot.addChangeListener(this);
	},
	
	createMonthSlot: function() {
		this.monthSlotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.MONTH_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(this.monthSlotContainer);
		this.monthSlot = new MonthSlot(this.monthSlotContainer);
		this.monthSlot.addChangeListener(this);
	},
	
	createYearSlot: function() {
		this.yearSlotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.YEAR_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(this.yearSlotContainer);
		this.yearSlot = new YearSlot(this.yearSlotContainer, 1980, 2055);
		this.yearSlot.addChangeListener(this);
	},
	
	createSlotGap: function(w) {
		this.container.insert(new Element("DIV", {className:"slotGap", style:"width:" + w + "px"}));
	},
	
	isLeapYear: function(year) {
		return (new Date(year, 1, 29).getDate() == 29);
	},
	
	getMaxDaysOfMonth: function(month, year) {
		switch (month) 
		{
			case 3:
			case 5:
			case 8:
			case 10:
				return 30;
			case 1:
				return this.isLeapYear(year) ? 29 : 28;
			default:
				return 31;
		}
	},
	
	slotChanged: function(slot, item) {
		if (!this.settingDate)
		{
			var year 	= this.yearSlot.getValue();
			var month	= this.monthSlot.getValue();
			var day		= this.daySlot.getValue();
			this.date 	= new Date(year, month, day);
			if ((slot == this.monthSlot) || (slot == this.yearSlot && month == 1))
			{
				this.daySlot.setMaxIndex(this.getMaxDaysOfMonth(month, year) - 1);
			}
		}
	},
	
	setValue: function(date) {
		if (this.date != date) 
		{
			this.settingDate = true;
			this.daySlot.setValue(date.getDate());
			this.monthSlot.setSelectedIndex(date.getMonth());
			this.yearSlot.setValue(date.getFullYear());
			this.date = date;
			this.settingDate = false;
		}
	},
	
	getValue: function() {
		return this.date;
	},
	
	today: function() {
		this.setValue(new Date());
	}
	
});

HourSlots = Class.create({

	SLOT_BORDER_WIDTH	: 14,
	HOUR_SLOT_WIDTH		: 50,
	MINUTE_SLOT_WIDTH	: 50,
	TOTAL_WIDTH			: 0,
	TOTAL_GAP_WIDTH		: 25,
	MIN_HEIGHT			: 215,

	initialize: function(container) {
		this.initializeWidths();
		this.initializeContainer(container);
		this.createSlotGap(10);
		this.createHourSlot();
		this.createSlotGap(5);
		this.createMinuteSlot();
		this.createSlotGap(10);
		this.setValue();
	},
	
	initializeContainer: function(container) {
		this.container 	= $(container);
		var dimensions	= this.container.getDimensions();
		if (dimensions.width < this.TOTAL_WIDTH)
		{
			this.container.setStyle({width: this.TOTAL_WIDTH + "px"});
		}
		if (dimensions.height < this.MIN_HEIGHT)
		{
			this.container.setStyle({height: this.MIN_HEIGHT + "px"});
		}
	},
	
	initializeWidths: function() {
		this.HOUR_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.MINUTE_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.TOTAL_WIDTH		= this.HOUR_SLOT_WIDTH + this.MINUTE_SLOT_WIDTH + this.TOTAL_GAP_WIDTH;
	},
	
	createHourSlot: function() {
		this.hourSlotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.HOUR_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(this.hourSlotContainer);
		this.hourSlot = new NumericSlot(this.hourSlotContainer, 0, 23, 2);
		this.hourSlot.addChangeListener(this);
	},
	
	createMinuteSlot: function() {
		this.minuteSlotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.MINUTE_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(this.minuteSlotContainer);
		this.minuteSlot = new NumericSlot(this.minuteSlotContainer, 0, 59, 2);
		this.minuteSlot.addChangeListener(this);
	},
	
	createSlotGap: function(w) {
		this.container.insert(new Element("DIV", {className:"slotGap", style:"width:" + w + "px"}));
	},
	
	slotChanged: function(slot, item) {
		if (!this.settingDate)
		{
			var hour 	= this.hourSlot.getValue();
			var minute	= this.minuteSlot.getValue();
			this.date 	= new Date(0, 0, 0, hour, minute, 0, 0);
		}
	},
	
	setValue: function(date) {
		if (!date)
		{
			date = new Date();
			date.setFullYear(0);
			date.setMonth(0);
			date.setDate(0);
			date.setSeconds(0);
			date.setMilliseconds(0);
		}
		if (this.date != date) 
		{
			this.settingDate = true;
			this.hourSlot.setValue(date.getHours());
			this.minuteSlot.setValue(date.getMinutes());
			this.settingDate = false;
			this.date = date;
		}
	},
	
	getValue: function() {
		return this.date;
	},
	
	now: function() {
		this.setValue();
	}
	
});

DateTimeSlots = Class.create({

	initialize: function(dateContainer, hourContainer) {
		this.dateSlots 	= new DateSlots(dateContainer);
		this.hourSlots	= new HourSlots(hourContainer);
	},
	
	setValue: function(date) {
		this.dateSlots.setValue(date);
		this.hourSlots.setValue(date);
	},
	
	getValue: function() {
		var date = this.dateSlots.getValue();
		var hour = this.hourSlots.getValue();
		return new Date(
			date.getFullYear(),
			date.getMonth(),
			date.getDate(),
			hour.getHours(),
			hour.getMinutes(),
			0,
			0
		);
	}
});

ClockSlots = Class.create({

	SLOT_BORDER_WIDTH	: 14,
	HOUR_SLOT_WIDTH		: 50,
	MINUTE_SLOT_WIDTH	: 50,
	SECOND_SLOT_WIDTH	: 50,
	TOTAL_WIDTH			: 0,
	TOTAL_GAP_WIDTH		: 30,
	MIN_HEIGHT			: 215,

	initialize: function(container) {
		this.initializeWidths();
		this.initializeContainer(container);
		this.createSlotGap(10);
		this.createHourSlot();
		this.createSlotGap(5);
		this.createMinuteSlot();
		this.createSlotGap(5);
		this.createSecondSlot();
		this.createSlotGap(10);
		// this.setValue();
		this.tick();
	},
	
	initializeContainer: function(container) {
		this.container 	= $(container);
		var dimensions	= this.container.getDimensions();
		if (dimensions.width < this.TOTAL_WIDTH)
		{
			this.container.setStyle({width: this.TOTAL_WIDTH + "px"});
		}
		if (dimensions.height < this.MIN_HEIGHT)
		{
			this.container.setStyle({height: this.MIN_HEIGHT + "px"});
		}
	},
	
	initializeWidths: function() {
		this.HOUR_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.MINUTE_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.SECOND_SLOT_WIDTH	+= this.SLOT_BORDER_WIDTH;
		this.TOTAL_WIDTH		= this.HOUR_SLOT_WIDTH + this.MINUTE_SLOT_WIDTH + this.SECOND_SLOT_WIDTH + this.TOTAL_GAP_WIDTH;
	},
	
	createCommonSlot: function(min, max) {
		var slotContainer = new Element("DIV", {
			className	: "commonSlotContainer",
			style		: "width:" + this.HOUR_SLOT_WIDTH + "px; float:left"
		});
		this.container.insert(slotContainer);
		slot = new NumericSlot(slotContainer, min, max, 2);
		slot.addChangeListener(this);
		slot.stop();
		return slot;
	},
	
	createHourSlot: function() {
		this.hourSlot = this.createCommonSlot(0, 23);
	},
	
	createMinuteSlot: function() {
		this.minuteSlot = this.createCommonSlot(0, 59);
	},
	
	createSecondSlot: function() {
		this.secondSlot = this.createCommonSlot(0, 59);
	},
	
	createSlotGap: function(w) {
		this.container.insert(new Element("DIV", {className:"slotGap", style:"width:" + w + "px"}));
	},
	
	getValue: function() {
		var hour 	= this.hourSlot.getValue();
		var minute	= this.minuteSlot.getValue();
		var second	= this.secondSlot.getValue();
		return new Date(0, 0, 0, hour, minute, second, 0);
	},
	
	now: function() {
		this.setValue();
	},
	
	tick: function() {
		var date = new Date();
		this.hourSlot.setValue(date.getHours());
		this.minuteSlot.setValue(date.getMinutes());
		this.secondSlot.setValue(date.getSeconds());
		this.tick.bind(this).delay(1);
	}
	
});
